﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Linq;
using XCommunity.Documents;
using System.Xml;

namespace XCommunity.Frames
{


  /// <summary>
  /// 对ITopicFrame的一个一般性实现
  /// </summary>
  public class GeneralTopicFrame : IPersistableTopicFrame
  {
    public GeneralTopicFrame( FrameService service, TopicMeta meta, ICommunitySystem communitySystem )
    {
      Service = service;
      Meta = meta;
      CommunitySystem = communitySystem;

      Saved = false;
    }



    /// <summary>
    /// 获取框架服务
    /// </summary>
    public FrameService Service
    {
      get;
      private set;
    }

    /// <summary>
    /// 获取持久化对象
    /// </summary>
    protected TopicPersister Persister
    {
      get { return Service.TopicPersister; }
    }

    /// <summary>
    /// 获取对话题框架有控制权的社区系统
    /// </summary>
    public ICommunitySystem CommunitySystem
    {
      get;
      private set;
    }



    /// <summary>
    /// 话题ID
    /// </summary>
    public int TopicId
    {
      get { return Meta.TopicId; }

    }



    private Guid? _guid;

    /// <summary>
    /// 话题的唯一标识
    /// </summary>
    public Guid Guid
    {
      get { return Meta.Guid; }
    }



    /// <summary>
    /// 获取话题的元数据
    /// </summary>
    /// <returns></returns>
    public TopicMeta Meta
    {
      get;
      private set;
    }



    /// <summary>
    /// 指示话题是否加载完毕或已经保存
    /// </summary>
    public bool Saved
    {
      get;
      private set;
    }




    private GeneralPostFrame _theme;

    /// <summary>
    /// 获取话题的主题
    /// </summary>
    /// <exception cref="System.InvalidOperationException">如果话题未被加载或者尚未保存</exception>
    public GeneralPostFrame Theme
    {
      get
      {
        if ( !Saved )
          throw new InvalidOperationException();

        return _theme;
      }
    }



    private List<GeneralPostFrame> _responses;


    /// <summary>
    /// 获取所有回应。
    /// </summary>
    /// <exception cref="System.InvalidOperationException">如果话题未被加载或者尚未保存</exception>
    public IEnumerable<GeneralPostFrame> Responses
    {
      get
      {
        if ( !Saved )
          throw new InvalidOperationException();

        return _responses;
      }
    }



    #region ITopicFrame

    IPostFrame ITopicFrame.Theme
    {
      get { return Theme; }
    }

    IEnumerable<IPostFrame> ITopicFrame.Responses
    {
      get { return Responses.Cast<IPostFrame>(); }
    }

    TopicMeta ITopicFrame.Meta
    {
      get { return Meta; }
    }

    #endregion



    /// <summary>
    /// 从一个流中加载框架数据
    /// </summary>
    /// <param name="stream">用于加载框架数据的流</param>
    public void Load( Stream stream )
    {
      XElement frameData = XElement.Load( new StreamReader( stream, Encoding.UTF8 ) );

      LoadFrame( frameData );

      Saved = true;
    }


    /// <summary>
    /// 保存框架数据到一个流
    /// </summary>
    /// <param name="stream">用于保存框架数据的流</param>
    public void Save( Stream stream )
    {
      XElement frameData = SaveFrame();

      using ( XmlWriter writer = XmlWriter.Create( stream, new XmlWriterSettings() { Encoding = Encoding.UTF8, OmitXmlDeclaration = true, Indent = true } ) )
      {
        frameData.Save( writer );
      }

      Saved = true;
    }



    /// <summary>
    /// 载入框架数据
    /// </summary>
    /// <param name="frameData">框架数据</param>
    protected virtual void LoadFrame( XElement frameData )
    {
      //TODO 使得原来所有的帖子对象全部失效

      _allPosts = null;

      _theme = LoadPost( frameData.Element( "Theme" ).Element( "Post" ) );
      _responses = frameData.Element( "Responses" ).Elements( "Post" ).Select( postElement => LoadPost( postElement ) ).ToList();
    }



    /// <summary>
    /// 通过框架数据加载帖子
    /// </summary>
    /// <param name="postFrameData">帖子框架数据</param>
    /// <returns></returns>
    protected virtual GeneralPostFrame LoadPost( XElement postFrameData )
    {
      GeneralPostFrame post = new GeneralPostFrame( this );
      post.Load( postFrameData );

      return post;
    }



    /// <summary>
    /// 创建框架数据用于保存
    /// </summary>
    /// <returns></returns>
    protected virtual XElement SaveFrame()
    {
      XElement frameData = new XElement( "Topic" );
      frameData.Add( new XAttribute( "GUID", Guid ) );
      frameData.Add( new XElement( "Theme", _theme.Save() ) );

      XElement responsesElement = new XElement( "Responses", _responses.Select( post => post.Save() ) );
      frameData.Add( responsesElement );

      return frameData;
    }





    /// <summary>
    /// 需要发起话题时，创建话题对象后，调用此方法初始化框架数据。
    /// </summary>
    /// <param name="document"></param>
    public virtual void LaunchedFrom( Document document )
    {

      if ( _theme != null )
        throw new InvalidOperationException();//UNDONE 异常详细信息

      PublishedDocument publishedDocument = Service.PublishDocument( this, document, "LaunchTopic" );

      _theme = CreateTheme( publishedDocument );

      _responses = new List<GeneralPostFrame>();
    }



    /// <summary>
    /// 创建主题帖子
    /// </summary>
    /// <param name="document">用于创建主题的文档</param>
    /// <returns></returns>
    protected virtual GeneralPostFrame CreateTheme( PublishedDocument document )
    {
      GeneralPostFrame post = new GeneralPostFrame( this );
      post.Load( CreatePostFrame( document ) );

      return post;
    }



    /// <summary>
    /// 创建回复帖子
    /// </summary>
    /// <param name="document">用于创建回复的文档</param>
    /// <returns></returns>
    protected virtual GeneralPostFrame CreateResponse( PublishedDocument document )
    {
      GeneralPostFrame post = new GeneralPostFrame( this );
      post.Load( CreatePostFrame( document ) );

      return post;
    }



    /// <summary>
    /// 创建帖子的框架数据
    /// </summary>
    /// <param name="document"></param>
    /// <returns></returns>
    protected virtual XElement CreatePostFrame( PublishedDocument document )
    {

      XElement frameData = new XElement( "Post" );

      frameData.Add
      (
        new XAttribute( "GUID", Guid.NewGuid() ),
        new XAttribute( "PublishDate", DateTime.UtcNow.ToString( "u" ) ),
        new XElement( "Author", document.GetDocument().Meta.Author ),
        new XElement( "Current",
          new XElement( "Document",
            new XAttribute( "ID", document.DocumentId )
          )
        )
      );

      return frameData;
    }


    /// <summary>
    /// 加入这个个话题的讨论
    /// </summary>
    /// <param name="document"></param>
    /// <returns></returns>
    public GeneralPostFrame AttachTopic( Document document )
    {
      PublishedDocument _document = Service.PublishDocument( this, document, "AttachTopic" );

      GeneralPostFrame response = CreateResponse( _document );


      using ( Stream stream = Persister.GetFrameDataStream( this ) )
      {
        Load( stream );

        _responses.Add( response );
        _allPosts = null;

        stream.SetLength( 0 );

        Save( stream );
      }

      OnTopicAttached();

      OnTopicChanged();

      return response;
    }


    /// <summary>
    /// 引发TopicAttached事件
    /// </summary>
    protected virtual void OnTopicAttached()
    {
      OnTopicAttached( this, CreateEventArgs() );
    }

    /// <summary>
    /// 引发TopicAttached事件
    /// </summary>
    protected virtual void OnTopicAttached( object sender, TopicEventArgs e )
    {
      Service.OnTopicAttached( sender, e );
    }


    /// <summary>
    /// 修改一个帖子
    /// </summary>
    /// <param name="postId">帖子ID</param>
    /// <param name="document">修改后的文档版本</param>
    /// <returns></returns>
    public GeneralPostFrame ModifyPost( Guid postId, Document document )
    {

      if ( !AllPosts.ContainsKey( postId ) )
        throw new ArgumentException();//UNDONE 异常详细信息

      PublishedDocument publishedDocument = Service.PublishDocument( this, document, "ModifyPost" );


      GeneralPostFrame post;

      using ( Stream stream = Persister.GetFrameDataStream( this ) )
      {
        Load( stream );

        post = GetPost( postId );

        post.ExecuteModifyPost( publishedDocument );

        stream.SetLength( 0 );

        Save( stream );
      }

      OnPostModified( post );

      OnTopicChanged();

      return post;
    }


    /// <summary>
    /// 引发PostModified的事件
    /// </summary>
    /// <param name="post"></param>
    protected void OnPostModified( GeneralPostFrame post )
    {
      OnPostModified( this, new PostEventArgs() { Account = Service.Context.CurrentAccount, Post = post } );
    }

    /// <summary>
    /// 引发PostModified的事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void OnPostModified( object sender, PostEventArgs e )
    {
      Service.OnPostModified( sender, e );
    }




    protected GeneralPostFrame GetResponse( Guid postId )
    {

      GeneralPostFrame response;
      if ( AllPosts.TryGetValue( postId, out response ) )
        throw new ArgumentException();//UNDONE 异常详细信息

      if ( response.IsTheme )
        throw new InvalidOperationException();//UNDONE 异常详细信息

      return response;
    }



    /// <summary>
    /// 将一个回应从话题中移除
    /// </summary>
    /// <param name="postId">帖子ID</param>
    /// <remarks>注意本方法不能用于移除话题的主题（Theme）</remarks>
    public void RemoveReponse( Guid postId )
    {

      var post = GetResponse( postId );

      if ( post.Removed )
        return;

      ExecutePostChange( delegate { post.ExecuteRemovePost(); } );

    }

    /// <summary>
    /// 将一个回复隐藏
    /// </summary>
    /// <param name="postId">帖子ID</param>
    public void HideResponse( Guid postId )
    {

      var post = GetResponse( postId );

      if ( post.Hidden )
        return;

      ExecutePostChange( delegate { post.ExecuteHidePost(); } );
    }

    /// <summary>
    /// 将一个回复取消隐藏
    /// </summary>
    /// <param name="postId">帖子ID</param>
    public void UnhideResponse( Guid postId )
    {
      var post = GetResponse( postId );

      if ( !post.Hidden )
        return;


      ExecutePostChange( delegate { post.ExecuteUnhidePost(); } );
    }


    protected void ExecutePostChange( Action executer )
    {
      using ( Stream stream = Persister.GetFrameDataStream( this ) )
      {
        Load( stream );

        executer();

        stream.SetLength( 0 );

        Save( stream );
      }

      OnTopicChanged();
    }





    private Dictionary<Guid, GeneralPostFrame> _allPosts = null;

    /// <summary>
    /// 获取所有的帖子
    /// </summary>
    protected Dictionary<Guid, GeneralPostFrame> AllPosts
    {
      get
      {
        if ( _allPosts == null )
        {
          _allPosts = new Dictionary<Guid, GeneralPostFrame>();

          _allPosts.Add( _theme.Guid, _theme );
          foreach ( GeneralPostFrame post in _responses )
            _allPosts.Add( post.Guid, post );
        }

        return _allPosts;
      }
    }

    /// <summary>
    /// 获取帖子
    /// </summary>
    /// <param name="postId">帖子ID</param>
    /// <returns></returns>
    public GeneralPostFrame GetPost( Guid postId )
    {
      return AllPosts[postId];
    }



    /// <summary>
    /// 引发TopicChanged事件
    /// </summary>
    protected void OnTopicChanged()
    {
      OnTopicChanged( this, CreateEventArgs() );
    }



    /// <summary>
    /// 引发TopicChanged事件
    /// </summary>
    protected void OnTopicChanged( object sender, TopicEventArgs e )
    {
      Service.OnTopicChanged( sender, e );
    }


    /// <summary>
    /// 创建话题事件参数
    /// </summary>
    /// <returns></returns>
    protected TopicEventArgs CreateEventArgs()
    {
      return new TopicEventArgs() { Account = Service.Context.CurrentAccount, Topic = this };
    }


    /// <summary>
    /// 修改话题的标题
    /// </summary>
    /// <param name="p"></param>
    public void ModifyTitle( string title )
    {
      Persister.DataProvider.ModifyTitle( TopicId, title );

      Service.TopicMetaCache.RemoveCacheItem( TopicId );

      Meta = Service.GetTopicMeta( TopicId );
    }
  }
}
